using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using UwinEducation.Application.Common.Interfaces;

using UwinEducation.Shared;
using UwinEducation.Shared.Models.Auth;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.AspNetCore.Http;
using UwinEducation.Domain.Entities;
using UwinEducation.Domain.IRepositories;

namespace UwinEducation.Application.Services
{

    public class AuthService : IAuthService
    {
        private readonly IConfiguration _configuration;
        private readonly ICacheService _cacheService;
        private readonly IRepository<User, Guid> _userRepository;
        private readonly IPasswordHasher _passwordHasher;
        private readonly IDistributedCache _cache;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IRolePermissionRepository _rolePermissionRepository;

        public AuthService(
            IConfiguration configuration,
            ICacheService cacheService,
            IRepository<User, Guid> userRepository,
            IPasswordHasher passwordHasher,
            IDistributedCache cache,
            IHttpContextAccessor httpContextAccessor,
            IRolePermissionRepository rolePermissionRepository)
        {
            _configuration = configuration;
            _cacheService = cacheService;
            _userRepository = userRepository;
            _passwordHasher = passwordHasher;
            _cache = cache;
            _httpContextAccessor = httpContextAccessor;
            _rolePermissionRepository = rolePermissionRepository;
        }

        public async Task<TokenResponse> LoginAsync(LoginRequest request)
        {
            // 验证用户名密码
            var user = await _userRepository.Query()
                .FirstOrDefaultAsync(u => u.UserName == request.Username);
            if (user == null)
            {
                throw new ArgumentException("用户不存在");
            }
            if (!_passwordHasher.VerifyPassword(request.Password, user.PasswordHash))
            {
                throw new ArgumentException("用户名或密码错误");
            }

            // 获取用户权限列表
            var permissions = await _rolePermissionRepository.GetPermissionCodesByUserIdAsync(user.Id);

            // 生成Token
            var token = await GenerateTokenAsync(user);
            token.Permissions = permissions;

            // 缓存刷新令牌
            await CacheRefreshTokenAsync(token.RefreshToken, user.Id);

            return token;
        }

        public async Task<TokenResponse> RefreshTokenAsync(string refreshToken)
        {
            // 从缓存获取用户ID
            var userId = await _cacheService.GetAsync<Guid>($"refresh_token:{refreshToken}");
            if (userId == Guid.Empty)
            {
                throw new ArgumentException("刷新令牌无效或已过期");
            }

            // 获取用户信息
            var user = await _userRepository.GetByIdAsync(userId);
            if (user == null)
            {
                throw new ArgumentException("用户不存在");
            }

            // 获取用户权限列表
            var permissions = await _rolePermissionRepository.GetPermissionCodesByUserIdAsync(user.Id);

            // 生成新Token
            var token = await GenerateTokenAsync(user);
            token.Permissions = permissions;

            // 更新缓存
            await _cacheService.RemoveAsync($"refresh_token:{refreshToken}");
            await CacheRefreshTokenAsync(token.RefreshToken, user.Id);

            return token;
        }

        public async Task LogoutAsync(string accessToken)
        {
            // 将访问令牌加入黑名单
            var tokenHash = ComputeHash(accessToken);
            await _cacheService.SetAsync($"blacklist:{tokenHash}", true,
                TimeSpan.FromMinutes(15)); // 设置适当的过期时间
        }

        private async Task<TokenResponse> GenerateTokenAsync(User user)
        {
            var token = await GenerateJwtTokenAsync(user);
            var tokenString = new JwtSecurityTokenHandler().WriteToken(token);

            // 存储有效的 token
            await _cacheService.SetAsync(
                $"valid_token:{user.Id}",
                tokenString,
                TimeSpan.FromMinutes(int.Parse(_configuration["Jwt:AccessTokenExpiration"] ?? "1440"))
            );

            return new TokenResponse
            {
                AccessToken = tokenString,
                RefreshToken = GenerateRefreshToken(),
                ExpiresIn = (int)(token.ValidTo - DateTime.Now).TotalMinutes,
                UserId = user.Id,
                UserName = user.UserName,
                RealName = user.RealName ?? string.Empty

            };
        }

        private async Task CacheRefreshTokenAsync(string refreshToken, Guid userId)
        {
            var expirationMinutes = _configuration["Jwt:AccessTokenExpiration"];
            if (string.IsNullOrEmpty(expirationMinutes))
            {
                expirationMinutes = "1440";
            }

            await _cacheService.SetAsync(
                $"refresh_token:{refreshToken}",
                userId,
                TimeSpan.FromMinutes(int.Parse(expirationMinutes)) // 刷新令牌3天过期
            );
        }

        private string GenerateRefreshToken()
        {
            return Guid.NewGuid().ToString("N");
        }

        private string ComputeHash(string input)
        {
            using var sha256 = System.Security.Cryptography.SHA256.Create();
            var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(input));
            return Convert.ToBase64String(bytes);
        }


        private async Task<JwtSecurityToken> GenerateJwtTokenAsync(User user)
        {


            //            var claims = new[]
            //{
            //            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            //            new Claim(ClaimTypes.Name, user.UserName),
            //            new Claim(ClaimTypes.Email, user.Email)
            //        };

            //            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
            //            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            //            var expires = DateTime.UtcNow.AddMinutes(15); // 访问令牌15分钟过期

            //            var token = new JwtSecurityToken(
            //                issuer: _configuration["Jwt:Issuer"],
            //                audience: _configuration["Jwt:Audience"],
            //                claims: claims,
            //                expires: expires,
            //                signingCredentials: credentials
            //            );

            var expirationMinutes = _configuration["Jwt:AccessTokenExpiration"] ?? "1440";
            if (!int.TryParse(expirationMinutes, out var minutes))
            {
                minutes = 1440; // 默认 24 小时
            }


            var claims = new[]
            {
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new Claim(ClaimTypes.Name, user.UserName)
            };

            var token = new JwtSecurityToken(
                issuer: _configuration["Jwt:Issuer"],
                audience: _configuration["Jwt:Audience"],
                claims: claims,
                expires: DateTime.Now.AddMinutes(minutes),
                signingCredentials: new SigningCredentials(
                    new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])),
                    SecurityAlgorithms.HmacSha256)
            );

            return token;
        }
        private string GetCurrentAccessToken()
        {
            return _httpContextAccessor.HttpContext?.Request.Headers["Authorization"]
                .ToString().Replace("Bearer ", "");
        }

        private async Task InvalidateTokenAsync(string token)
        {
            var tokenHash = ComputeHash(token);
            var handler = new JwtSecurityTokenHandler();
            var jwtToken = handler.ReadJwtToken(token);
            var remainingTime = jwtToken.ValidTo - DateTime.UtcNow;

            if (remainingTime > TimeSpan.Zero)
            {
                await _cacheService.SetAsync(
                    $"blacklist:{tokenHash}",
                    true,
                    remainingTime
                );
            }
        }
    }
}